package org.javahispano.javacup.tacticas_aceptadas.blacksmith;

import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
import java.util.Random;

import org.javahispano.javacup.modelo.Comando;
import org.javahispano.javacup.modelo.ComandoGolpearBalon;
import org.javahispano.javacup.modelo.ComandoIrA;
import org.javahispano.javacup.modelo.Constantes;
import org.javahispano.javacup.modelo.Posicion;
import org.javahispano.javacup.modelo.SituacionPartido;
import org.javahispano.javacup.modelo.Tactica;
import org.javahispano.javacup.modelo.TacticaDetalle;

public class TacticaAldeaGala implements Tactica {
	
	/*
	 * DESCRIPCI�N DE LA T�CTICA
	 * -------------------------
	 * 
	 * El equipo se divide en Portero, Defensa, Centro y Delantera siguiendo un esquema 1 - 4 - 4 - 2
	 * 
	 * Portero:
	 * - Si el bal�n se dirige a la porter�a, colocarse para interceptarlo.
	 * - Si el bal�n se acerca a menos de X metros, dirigirse a �l.
	 * - En cuanto el bal�n est� a su alcance: 
	 * 		+ Si alguno de los defensas est� desmarcado (ning�n contrario en la trayectoria), pasarle.
	 * 		+ En caso contrario, patap�n pa arriba.
	 * 
	 * Defensa:
	 * - Se define una 'zona de peligro' en la parte baja del campo.
	 * - Si un contrario est� en la zona de peligro y sin marcar, el defensa m�s cercano lo marca.
	 * - Marcar un contrario consiste en situarse en su misma vertical y ligeramente atrasado.
	 * - Si el bal�n se acerca a menos de X metros, dirigirse a �l.
	 * - En cuanto el bal�n est� a su alcance:
	 * 	 	+ Si alguno de los centros est� desmarcado (ning�n contrario en la trayectoria), pasarle.
	 * 		+ En caso contrario, patap�n pa arriba.
	 * 
	 * Centro:
	 * - Si el bal�n se acerca a menos de X metros, dirigirse a �l.
	 * - En cuanto el bal�n est� a su alcance:
	 * 		+ Si alguno de los delanteros est� desmarcado (ning�n contrario en la trayectoria), pasarle.
	 * 		+ En caso contrario, si otro centro est� desmarcado, pasarle.
	 * 		+ En caso contrario, avanzar con el bal�n direcci�n el pico del �rea m�s cercano.
	 * 		+ En caso contrario, si un rival est� muy cerca, patap�n pa arriba.
	 * 
	 * Delantera:
	 * - Si el bal�n se acerca a menos de X metros, dirigirse a �l.
	 * - Si el otro delantero tiene el bal�n, avanzar direcci�n el pico del �rea m�s cercano
	 * - En cuanto el bal�n est� a su alcance:
	 * 		+ Si la porter�a est� vac�a chutar al centro.
	 * 		+ Si estamos a m�s de X metros, avanzar.
	 * 		+ Si estamos a menos de X metros, aleatoriamente:
	 * 			- Chutar al mejor hueco.
	 * 			- Pasar al pico del �rea m�s cercano al otro delantero.
	 */

	Posicion alineacion1[]=new Posicion[]{
			new Posicion(0.2595419847328244,-50.41044776119403),
			new Posicion(-20.20979020979021,-36.58371040723982),
			new Posicion(-9.510489510489512,-29.457013574660635),
			new Posicion(9.510489510489512,-29.21945701357466),
			new Posicion(19.97202797202797,-35.63348416289593),
			new Posicion(-28.531468531468533,-18.29185520361991),
			new Posicion(-15.216783216783217,-9.502262443438914),
			new Posicion(14.503496503496503,-9.264705882352942),
			new Posicion(27.34265734265734,-18.054298642533936),
			new Posicion(-4.9930069930069925,-4.038461538461538),
			new Posicion(1.4265734265734267,-1.6628959276018098)
	};

	Posicion alineacion2[]=new Posicion[]{
			new Posicion(0.2595419847328244,-50.41044776119403),
			new Posicion(-20.685314685314687,-35.15837104072398),
			new Posicion(-9.986013986013985,-29.21945701357466),
			new Posicion(9.272727272727272,-28.98190045248869),
			new Posicion(20.20979020979021,-35.15837104072398),
			new Posicion(-26.867132867132867,-18.29185520361991),
			new Posicion(-14.74125874125874,-8.314479638009049),
			new Posicion(14.74125874125874,-8.789592760180994),
			new Posicion(27.34265734265734,-18.529411764705884),
			new Posicion(-5.230769230769231,-9.739819004524888),
			new Posicion(4.755244755244756,-9.264705882352942)
	};

	Posicion alineacion3[]=new Posicion[]{
			new Posicion(0.2595419847328244,-50.41044776119403),
			new Posicion(-27.34265734265734,-22.330316742081447),
			new Posicion(-12.125874125874127,-31.83257918552036),
			new Posicion(11.65034965034965,-31.59502262443439),
			new Posicion(21.874125874125873,-22.092760180995477),
			new Posicion(-25.202797202797203,0.23755656108597287),
			new Posicion(-9.272727272727272,0.47511312217194573),
			new Posicion(8.797202797202797,0.47511312217194573),
			new Posicion(26.62937062937063,0.47511312217194573),
			new Posicion(-11.888111888111888,31.119909502262445),
			new Posicion(12.839160839160838,30.407239819004527)
	};
	
	// Cada puesto que puede tener un jugador
	static final int PORTERO = 0;
	static final int DEFENSA = 1;
	static final int MEDIO = 2;
	static final int DELANTERO = 3;
	
	// Correspondencia entre cada jugador y su puesto
	static final int[] PUESTOS = {PORTERO, DEFENSA, DEFENSA, DEFENSA, DEFENSA, MEDIO, MEDIO, MEDIO, MEDIO, DELANTERO, DELANTERO};
	
	// Distancia a la que un jugador va a por el balon
	static final int DISTANCIA_A_BALON = 10;
	
	// Si un rival est� a esta distancia, se suelta el bal�n
	static final int DISTANCIA_RIVAL = 3;
	
	// Si el delantero est� a m�s de esta distancia, avanzar� (no pasar� ni chutar�)
	static final int DISTANCIA_MAX_A_PORTERIA = 25;
	
	// Relacion entre la distancia entre jugadores y la fuerza del pase
	static final double RELACION_DISTANCIA_FUERZA_PASE = 30;

	// Relacion entre la distancia entre jugadores y la fuerza del chute
	static final double RELACION_DISTANCIA_FUERZA_CHUTE = 20;

	TacticaDetalle detalle=new TacticaAldeaGalaDetalle();
	
	Random rand = new Random();
	
	Posicion ultimaPosBalon = Constantes.centroCampoJuego;
	
	LinkedList<Comando> comandos = new LinkedList<Comando>();

	public TacticaDetalle getDetalle() {
		return detalle;
	}

	public Posicion[] getPosicionSaca(SituacionPartido sp) {
		return alineacion1;
	}

	public Posicion[] getPosicionRecive(SituacionPartido sp) {
		return alineacion2;
	}

	public List<Comando> ejecutar(SituacionPartido sp) {
        
		// Se vacia la lista de comandos
        comandos.clear();
        
    	// Posiciones de cada puesto
    	Posicion[] DEFENSAS = {sp.misJugadores()[1], sp.misJugadores()[2], sp.misJugadores()[3], sp.misJugadores()[4]};
    	Posicion[] MEDIOS = {sp.misJugadores()[5], sp.misJugadores()[6], sp.misJugadores()[7], sp.misJugadores()[8]};
    	Posicion[] DELANTEROS = {sp.misJugadores()[9], sp.misJugadores()[10]};
    	
    	// Posiciones de los picos del �recha chica contraria
    	Posicion[] PICOS = {new Posicion(-Constantes.LARGO_AREA_CHICA / 2, Constantes.LARGO_CAMPO_JUEGO / 2), new Posicion(Constantes.LARGO_AREA_CHICA / 2, Constantes.LARGO_CAMPO_JUEGO / 2)};
        
        // Cada jugador a su posici�n
        for (int i=0; i<11; i++) {
            comandos.add(new ComandoIrA(i, alineacion3[i]));
        }  
        
        // PORTERO
        // -------

        // Si el bal�n se dirige a la porter�a, colocarse para interceptarlo.
        if(!sp.balon().equals(ultimaPosBalon) && sp.balon().getY()<ultimaPosBalon.getY()){
        	// Tenemos dos rectas:
        	// 		- �ltima posici�n del bal�n y posici�n actual del bal�n
        	//		- Los dos corner inferiores
        	// Si el punto de intersecci�n entre ambas rectas est� en mi porter�a significa
        	// que el bal�n viene a gol. Se mueve al portero a dicha posici�n de intersecci�n
        	Posicion interseccion = TacticaAldeaGalaUtil.Interseccion(ultimaPosBalon, sp.balon(), Constantes.cornerInfIzq, Constantes.cornerInfDer);
        	if(interseccion.getX()>=-Constantes.LARGO_ARCO/2 && interseccion.getX()<=Constantes.LARGO_ARCO/2){
        		comandos.add(new ComandoIrA(0, interseccion));
        		//System.out.println("PORTERO: INTERSECO X: " + interseccion.getX() + " Y: " + interseccion.getY());
        	}
        }
                
        // DEFENSA
        // -------
        
        // Se obtienen los delanteros a marcar
        Posicion[] rivalesAMarcar = TacticaAldeaGalaUtil.delanterosAMarcar(sp);
        // Lista de defensas que est�n marcando (para evitar que quieran marcar a dos rivales a la vez)
        ArrayList<Integer> defensasMarcando = new ArrayList<Integer>();
        // El defensa m�s cercano a cada rival a marcar, se encarga de marcarlo
        for(int i=0; i<rivalesAMarcar.length && defensasMarcando.size()<DEFENSAS.length; i++){
        	Posicion rivalAMarcar = rivalesAMarcar[i];
        	int[] defensasMarcandoArray = new int[defensasMarcando.size()];
        	for(int j=0; j<defensasMarcando.size(); j++){
        		defensasMarcandoArray[j] = defensasMarcando.get(j);
        	}
        	int defensaMasCercano = rivalAMarcar.indiceMasCercano(DEFENSAS, defensasMarcandoArray);
        	defensasMarcando.add(defensaMasCercano);
        	comandos.add(new ComandoIrA(1+defensaMasCercano, rivalAMarcar.moverPosicion(0, -3)));
//        	System.out.println("Jugador " + (1+defensaMasCercano) + " se pone a marcar");
        }
        
        // DELANTERA
        // ---------
        
        // Si el otro delantero tiene el bal�n, avanzar direcci�n el pico del �rea m�s cercano
        for(int delantero=9; delantero<11; delantero++){
        	int otroDelantero = delantero==10 ? 9 : 10;
        	int[] rematadores = sp.puedenRematar();
        	for(int j=0; j<rematadores.length; j++){
        		if(rematadores[j]==otroDelantero){
        			int indPicoCercano = sp.misJugadores()[delantero].indiceMasCercano(PICOS);
        			Posicion picoCercano = PICOS[indPicoCercano];
        			comandos.add(new ComandoIrA(delantero, picoCercano));
        			break;
        		}
        		
        	}
        }
        
        
        // TODOS
        // -----
        
        // Si el bal�n se acerca a menos de X metros, dirigirse a �l
        for(int i=0; i<11; i++){
            if (sp.misJugadores()[i].distancia(sp.balon()) <= DISTANCIA_A_BALON){
            	comandos.add(new ComandoIrA(i, sp.balon()));
            }        	
        }
        // El jugador mas cercano sigue al balon
        int indMasCercano = sp.balon().indiceMasCercano(sp.misJugadores());
        comandos.add(new ComandoIrA(indMasCercano, sp.balon()));
        // En cuanto el bal�n est� al alcance de alg�n jugador...
        int[] rematadores = sp.puedenRematar();
        for(int i=0; i<rematadores.length; i++){
        	int rematador = rematadores[i];
            boolean pase = false; // Indica si este rematador va a realizar un pase
            boolean chute = false; // Indica si este rematador va a realizar un chute
        	switch(PUESTOS[rematador]){
        	case PORTERO:
        		// Si alguno de los defensas est� desmarcado (ning�n contrario en la trayectoria) pasarle.
        		int[] defensas = sp.misJugadores()[rematador].indicesMasCercanos(DEFENSAS);
        		for(int j=0; j<defensas.length && !pase; j++){
        			int defensa = defensas[i];
        			if(!TacticaAldeaGalaUtil.rivalSeInterpone(sp.misJugadores()[rematador], DEFENSAS[defensa], sp.rivales())){
        				pase = true;
        				double distancia = sp.misJugadores()[rematador].distancia(DEFENSAS[defensa]);
        				double fuerza = distancia / RELACION_DISTANCIA_FUERZA_PASE;
        				boolean alto = distancia > 20;
        				comandos.add(new ComandoGolpearBalon(rematador, DEFENSAS[defensa], fuerza, alto));
        				//System.out.println("PORTERO: PASE A JUGADOR " + (defensa+1) + " FUERZA: " + fuerza + " ALTO: " + alto);
        			}
        		}
        		// En caso contrario, patap�n pa arriba.
        		if(!pase){
        			comandos.add(new ComandoGolpearBalon(rematador, Constantes.centroArcoSup , 1, true));
        			//System.out.println("PORTERO: PATAP�N");
        		}
        		break;
        	case DEFENSA:
        		// Si alguno de los medios est� desmarcado (ning�n contrario en la trayectoria) pasarle.
        		int[] medios = sp.misJugadores()[rematador].indicesMasCercanos(MEDIOS);
        		for(int j=0; j<medios.length && !pase; j++){
        			int medio = medios[i];
        			if(!TacticaAldeaGalaUtil.rivalSeInterpone(sp.misJugadores()[rematador], MEDIOS[medio], sp.rivales())){
        				pase = true;
        				double distancia = sp.misJugadores()[rematador].distancia(MEDIOS[medio]);
        				double fuerza = distancia / RELACION_DISTANCIA_FUERZA_PASE;
        				boolean alto = distancia > 20;
        				comandos.add(new ComandoGolpearBalon(rematador, MEDIOS[medio], fuerza, alto));
        				//System.out.println("DEFENSA: PASE A JUGADOR " + (medio+5) + " FUERZA: " + fuerza + " ALTO: " + alto);
        			}
        		}
        		// En caso contrario, patap�n pa arriba.
        		if(!pase){
        			comandos.add(new ComandoGolpearBalon(rematador, Constantes.centroArcoSup , 1, true));
        			//System.out.println("DEFENSA: PATAP�N");
        		}        		
        		break;
        	case MEDIO:
        		// Si alguno de los delanteros est� desmarcado (ning�n contrario en la trayectoria) pasarle.
        		int[] delanteros = sp.misJugadores()[rematador].indicesMasCercanos(DELANTEROS);
        		for(int j=0; j<delanteros.length && !pase; j++){
        			int delantero = delanteros[j];
        			if(!TacticaAldeaGalaUtil.rivalSeInterpone(sp.misJugadores()[rematador], DELANTEROS[delantero], sp.rivales())){
        				pase = true;
        				double distancia = sp.misJugadores()[rematador].distancia(DELANTEROS[delantero]);
        				double fuerza = distancia / RELACION_DISTANCIA_FUERZA_PASE;
        				boolean alto = distancia > 20;
        				comandos.add(new ComandoGolpearBalon(rematador, DELANTEROS[delantero], fuerza, alto));
        				//System.out.println("MEDIO: PASE A DELANTERO " + (delantero+9) + " FUERZA: " + fuerza + " ALTO: " + alto);
        			}
        		}
        		// En caso contrario, si otro centro est� desmarcado, pasarle.
        		if(!pase){
	        		medios = sp.misJugadores()[rematador].indicesMasCercanos(MEDIOS, rematador-5);
	        		for(int j=0; j<medios.length && !pase; j++){
	        			int medio = medios[i];
	        			if(!TacticaAldeaGalaUtil.rivalSeInterpone(sp.misJugadores()[rematador], MEDIOS[medio], sp.rivales())){
	        				pase = true;
	        				double distancia = sp.misJugadores()[rematador].distancia(MEDIOS[medio]);
	        				double fuerza = distancia / RELACION_DISTANCIA_FUERZA_PASE;
	        				boolean alto = distancia > 20;
	        				comandos.add(new ComandoGolpearBalon(rematador, MEDIOS[medio], fuerza, alto));
	        				//System.out.println("MEDIO: PASE A MEDIO " + (medio+5) + " FUERZA: " + fuerza + " ALTO: " + alto);
	        			}
	        		}        		
        		}
        		// En caso contrario, si un rival est� muy cerca, patap�n pa arriba.
        		if(!pase){
        			int indRivalCercano = sp.misJugadores()[rematador].indiceMasCercano(sp.rivales());
        			Posicion rivalCercano = sp.rivales()[indRivalCercano];
        			if(sp.misJugadores()[rematador].distancia(rivalCercano)<=DISTANCIA_RIVAL){
        				chute = true;
            			comandos.add(new ComandoGolpearBalon(rematador, Constantes.centroArcoSup , 1, true));
            			//System.out.println("MEDIO: PATAP�N");
        			}
        		}
        		// En caso contrario, avanzar con el bal�n direcci�n el pico del �rea peque�a m�s cercano
        		if(!pase && !chute){
        			int indPicoCercano = sp.misJugadores()[rematador].indiceMasCercano(PICOS);
        			Posicion picoCercano = PICOS[indPicoCercano];
        			comandos.add(new ComandoIrA(rematador, picoCercano));
        			comandos.add(new ComandoGolpearBalon(rematador));
        			//System.out.println("MEDIO: AVANZO CON EL BAL�N");
        		}
        		break;
        	case DELANTERO:
        		// Si la porter�a est� vac�a chutar al centro.
        		if(TacticaAldeaGalaUtil.estaPorteriaVacia(sp)){
        			chute = true;
    				double distancia = sp.misJugadores()[rematador].distancia(Constantes.centroArcoSup);
    				double fuerza = distancia / RELACION_DISTANCIA_FUERZA_CHUTE;
    				boolean alto = distancia > 20;
    				comandos.add(new ComandoGolpearBalon(rematador, Constantes.centroArcoSup, fuerza, alto));
    				//System.out.println("DELANTERO: CHUTE A PORTER�A VAC�A!!!");
        		}
        		// Si estamos a m�s de X metros, avanzar.
        		if(!chute && sp.misJugadores()[rematador].distancia(Constantes.centroArcoSup)>=DISTANCIA_MAX_A_PORTERIA){
        			int indPicoCercano = sp.misJugadores()[rematador].indiceMasCercano(PICOS);
        			Posicion picoCercano = PICOS[indPicoCercano];
        			comandos.add(new ComandoIrA(rematador, picoCercano));
        			comandos.add(new ComandoGolpearBalon(rematador));
        			//System.out.println("DELANTERO: AVANZO CON EL BAL�N");
        		}else if(!chute){
        			// Si estamos a menos de X metros, aleatoriamente
        			if(rand.nextDouble()>=0.25){
        				// Chutar al mejor hueco.
        				chute = true;
        				Posicion mejorHueco = TacticaAldeaGalaUtil.decidirDestinoChute(sp);
        				double distancia = sp.misJugadores()[rematador].distancia(mejorHueco);
        				double fuerza = distancia / RELACION_DISTANCIA_FUERZA_CHUTE;
        				boolean alto = distancia > 20;
        				comandos.add(new ComandoGolpearBalon(rematador, mejorHueco, fuerza, alto));
        				//System.out.println("DELANTERO: CHUTE AL MEJOR HUECO!!!");
        			}else{
        				// Pasar al pico del �rea m�s cercano al otro delantero.
        				pase = true;
        				int otroDelantero = rematador==10 ? 9 : 10;
        				int indPicoCercanoOtroDelantero = sp.misJugadores()[otroDelantero].indiceMasCercano(PICOS);
        				Posicion picoCercanoOtroDelantero = PICOS[indPicoCercanoOtroDelantero];
        				double distancia = sp.misJugadores()[rematador].distancia(picoCercanoOtroDelantero);
        				double fuerza = distancia / RELACION_DISTANCIA_FUERZA_PASE;
        				boolean alto = distancia > 20;
        				comandos.add(new ComandoGolpearBalon(rematador, picoCercanoOtroDelantero, fuerza, alto));
        				//System.out.println("DELANTERO: PASE A DELANTERO " + otroDelantero + " FUERZA: " + fuerza + " ALTO: " + alto);
        			}
        		}
        		break;
        		
        	}
        }
        
        // Se actualiza el valor de ultimaPosBalon
        ultimaPosBalon = sp.balon();
        
        // Se devuelve el resultado
        return comandos;
	}
	
}
